package top.yqingyu;

import ch.qos.logback.classic.LoggerContext;
import ch.qos.logback.core.Appender;
import ch.qos.logback.core.ConsoleAppender;
import ch.qos.logback.core.encoder.LayoutWrappingEncoder;
import ch.qos.logback.core.spi.AppenderAttachableImpl;
import ch.qos.logback.core.util.COWArrayList;

import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import okhttp3.*;
import com.tencentcloudapi.common.Credential;
import com.tencentcloudapi.common.exception.TencentCloudSDKException;
import com.tencentcloudapi.common.profile.ClientProfile;
import com.tencentcloudapi.common.profile.HttpProfile;
import com.tencentcloudapi.dnspod.v20210323.DnspodClient;
import com.tencentcloudapi.dnspod.v20210323.models.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import top.yqingyu.common.qydata.DataMap;
import top.yqingyu.common.utils.LocalDateTimeUtil;
import top.yqingyu.common.utils.StringUtil;
import top.yqingyu.common.utils.UnameUtil;
import top.yqingyu.common.utils.YamlUtil;

import java.io.*;
import java.lang.management.ManagementFactory;
import java.lang.management.RuntimeMXBean;
import java.lang.reflect.Field;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
import java.util.List;
import java.util.concurrent.FutureTask;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;


/**
 * @author YYJ
 * @version 1.0.0
 * @ClassName top.yqingyu.DDNS
 * @description
 * @createTime 2022年11月03日 23:20:00
 */
public class DDNS {

    public static final Logger log = LoggerFactory.getLogger(DDNS.class);
    private static String API_DOMAIN;
    private static String S_ID;
    private static String S_KEY;
    private static String IP_GET;
    private static String DOMAIN;
    private static String SUB_DOMAIN;
    private static Long RECORD_ID;


    private static final OkHttpClient client = new OkHttpClient();


    public static void main(String[] args) throws InterruptedException, NoSuchFieldException, IllegalAccessException {
        loggerCharsetConfig();
        YamlUtil yamlUtil = YamlUtil.loadYaml("ddns-config", YamlUtil.LoadType.OUTER);
        DataMap cfg = yamlUtil.getCfgData().getNotNUllData("ddns-config.yml");
        API_DOMAIN = cfg.getString("api-domain", "dnspod.tencentcloudapi.com");
        SUB_DOMAIN = cfg.getString("sub-name", "@");
        IP_GET = cfg.getString("ip-get", "http://42.192.75.54:4732/root/getIp");
        long INTERVAL = cfg.getLongValue("interval", 1400);
        S_ID = cfg.getString("s-id", "");
        S_KEY = cfg.getString("s-key", "");
        DOMAIN = cfg.getString("domain", "");
        String mode = System.getProperty("mode", "");
        if (StringUtil.isEmpty(mode)) {
            log.info("缺少运行时参数");
        } else if ("1".equals(mode)) {
            try {
                String userDir = System.getProperty("user.dir");
                String path = userDir + "/" + "recordID.txt";
                FileInputStream inputStream = new FileInputStream(path);
                byte[] bytes = inputStream.readAllBytes();
                inputStream.close();
                RECORD_ID = Long.parseLong(new String(bytes, StandardCharsets.UTF_8));
            } catch (Exception e) {
                log.info(
                        "请先使用模式2 recordID.txt 在根目录 执行 {}{}{}",
                        "\n./start.sh 2",
                        "或\n",
                        "java -Dmode=2 -jar ./src/ddns-1.0.1-jar-with-dependencies.jar");
                System.exit(-99);
            }
            for (; ; ) {
                LocalDateTime now = LocalDateTime.now();
                try {
                    FutureTask<Boolean> future = new FutureTask<>(() -> {
                        String currentIp = getCurrentIp();
                        String currentDnsIp = getRecordInfo();
                        if (!currentIp.equals(currentDnsIp)) {
                            RECORD_ID = modifyRecord(currentIp);
                        } else {
                            log.info("无需更新 IP: {} ,域名: {} ", currentDnsIp, DOMAIN);
                        }
                        return true;
                    });
                    Thread thread = new Thread(future);
                    thread.setName("DDNS-LOOP");
                    thread.start();
                    future.get(30, TimeUnit.SECONDS);
                } catch (Throwable e) {
                    handleException(e);
                }
                log.info("=================cost {}==============================", LocalDateTimeUtil.between(now, LocalDateTime.now()));
                Thread.sleep(INTERVAL);
            }
        } else if ("2".equals(mode)) {
            try {
                String currentIp = getCurrentIp();
                CreateRecord(currentIp);
            } catch (Throwable e) {
                handleException(e);
            }
        }
    }

    public static String getCurrentIp() throws IOException {
        Request.Builder builder = new Request.Builder();
        Request request = builder.get()
                .url(IP_GET)
                .build();
        LocalDateTime now = LocalDateTime.now();
        log.info("正在获取当前IP...........");
        Call call = client.newCall(request);
        call.timeout().timeout(31, TimeUnit.SECONDS);
        Response response = call.execute();
        ResponseBody body = response.body();
        String bodyStr = body.string();
        JSONObject bodyJson = JSON.parseObject(bodyStr);
        String currentIp = bodyJson.getString("data");
        log.info("当前IP公网: {} cost: {}", currentIp, LocalDateTimeUtil.between(now, LocalDateTime.now()));
        return currentIp;
    }

    /**
     * @author YYJ
     * @version 1.0.0
     * @description 获取记录信息
     */
    public static String getRecordInfo() throws TencentCloudSDKException {
        log.info("正在获取当前域名{} DNS IP...........", DOMAIN);
        LocalDateTime now = LocalDateTime.now();
        DescribeRecordRequest req = new DescribeRecordRequest();
        req.setDomain(DOMAIN);
        req.setRecordId(RECORD_ID);
        DescribeRecordResponse resp = createDnspodClient().DescribeRecord(req);
        String currentDnsIp = resp.getRecordInfo().getValue();
        log.info("域名: {} ,DNS IP: {} cost {}", DOMAIN, currentDnsIp, LocalDateTimeUtil.between(now, LocalDateTime.now()));
        return currentDnsIp;
    }


    public static Long modifyRecord(String nowIp) throws TencentCloudSDKException {
        log.info("正在更新DNS");
        LocalDateTime now = LocalDateTime.now();
        ModifyRecordRequest req = new ModifyRecordRequest();
        req.setDomain(DOMAIN);
        req.setSubDomain(SUB_DOMAIN);
        req.setRecordType("A");
        req.setRecordLine("默认");
        req.setValue(nowIp);
        req.setRecordId(RECORD_ID);
        ModifyRecordResponse resp = createDnspodClient().ModifyRecord(req);

        log.info("更新成功 当前IP {} cost {}", nowIp, LocalDateTimeUtil.between(now, LocalDateTime.now()));
        return resp.getRecordId();
    }


    public static void CreateRecord(String ip) throws TencentCloudSDKException, IOException {
        CreateRecordRequest req = new CreateRecordRequest();
        req.setSubDomain(SUB_DOMAIN);
        req.setDomain(DOMAIN);
        req.setRecordType("A");
        req.setRecordLine("默认");
        req.setValue(ip);
        CreateRecordResponse resp = createDnspodClient().CreateRecord(req);
        String userDir = System.getProperty("user.dir");
        String path = userDir + "/" + "recordID.txt";
        FileOutputStream stream = new FileOutputStream(path);
        stream.write(String.valueOf(resp.getRecordId()).getBytes(StandardCharsets.UTF_8));
        stream.flush();
        stream.close();
        log.info("创建成功 文件路径: {}", path);
    }


    public static DnspodClient createDnspodClient() {
        Credential cred = new Credential(S_ID, S_KEY);
        HttpProfile httpProfile = new HttpProfile();
        httpProfile.setEndpoint(API_DOMAIN);
        ClientProfile clientProfile = new ClientProfile();
        clientProfile.setHttpProfile(httpProfile);
        return new DnspodClient(cred, "", clientProfile);
    }

    public static void handleException(Throwable e) {
        if (e instanceof TencentCloudSDKException) {
            if (e.getMessage().contains("Please check your signature is correct")) {
                log.error("请检查 SecretId/SecretKey配置是否正确");
            } else {
                log.error("", e);
            }
        } else if (e instanceof TimeoutException) {
            log.error("LOOP超时，准备自重启", e);
            try {
                RuntimeMXBean runtimeMxBean = ManagementFactory.getRuntimeMXBean();
                List<String> inputArguments = runtimeMxBean.getInputArguments();
                StringBuilder stringBuilder = new StringBuilder();
                for (String inputArgument : inputArguments) {
                    stringBuilder.append(" ").append(inputArgument);
                }
                ProcessBuilder processBuilder = new ProcessBuilder();
                String java;
                String stdOut;
                if (UnameUtil.isWindows()) {
                    processBuilder.command("powershell");
                    java = "${JAVA_HOME}/bin/javaw ";
                    stdOut = "";
                } else {
                    processBuilder.command("bash");
                    java = "nohup ${JAVA_HOME}/bin/java ";
                    stdOut = ">> /dev/null 2>&1 &";
                }
                Process start = processBuilder.start();
                BufferedWriter bufferedWriter = start.outputWriter();
                String command = "cd " + System.getProperty("user.dir") + " && " + java + stringBuilder + " -jar " + System.getProperty("sun.java.command") + stdOut;
                log.error("执行启动命令 {}", command);
                bufferedWriter.write(command);
                bufferedWriter.flush();
                bufferedWriter.close();
            } catch (Exception ex) {
                log.error("重启失败", ex);
                return;
            }
            log.info("系统退出");
            System.exit(-99);
        } else {
            log.error("", e);
        }
    }

    public static void loggerCharsetConfig() throws NoSuchFieldException, IllegalAccessException {

        String encoding = System.getProperty("stdout.encoding");
        if (encoding == null) {
            encoding = System.getProperty("file.encoding");
        }
        if (!"UTF-8".equals(encoding)) {
            log.info("chinese out put test character encoding:{}  中文输出测试 字符编码：{}", encoding, encoding);
            LoggerContext context = (LoggerContext) LoggerFactory.getILoggerFactory();
            Class<LoggerContext> contextClass = LoggerContext.class;
            Field root = contextClass.getDeclaredField("root");
            root.setAccessible(true);
            ch.qos.logback.classic.Logger o = (ch.qos.logback.classic.Logger) root.get(context);
            Class<? extends ch.qos.logback.classic.Logger> loggerClass = o.getClass();
            Field aai = loggerClass.getDeclaredField("aai");
            aai.setAccessible(true);
            AppenderAttachableImpl o1 = (AppenderAttachableImpl) aai.get(o);
            Field appenderList = AppenderAttachableImpl.class.getDeclaredField("appenderList");
            appenderList.setAccessible(true);
            COWArrayList<Appender> o2 = (COWArrayList<Appender>) appenderList.get(o1);
            ConsoleAppender appender = (ConsoleAppender) o2.get(0);
            LayoutWrappingEncoder encoder = (LayoutWrappingEncoder) appender.getEncoder();
            encoder.setCharset(Charset.forName(encoding));
            log.info("chinese out put test character encoding:{}  中文输出测试 字符编码：{}", encoding, encoding);
            return;
        }
        log.info("chinese out put test character encoding:{}  中文输出测试 字符编码：{}", encoding, encoding);
    }
}
